#include "wave.h"

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <math.h>

#include "parameters.h"
#include "kill.h"

static int wave_width;

static uint32_t *channel0 = NULL;
static uint32_t *channel1 = NULL;
static int channel0write;
static int channel1write;

void callback_cleanup_wave() {
	free(channel0);
	free(channel1);
}

int wave_points() { return wave_width; }

void wave_init(int width) {
	wave_width = width;

	channel0 = (uint32_t*)malloc(sizeof(uint32_t) * wave_width);
	channel1 = (uint32_t*)malloc(sizeof(uint32_t) * wave_width);
	for (int i = 0; i < wave_width; ++i) {
		channel0[i] = channel1[i] = 0xFFFFFFFF / 2;
	}
	channel0write = channel1write = wave_width;

	kill_push(callback_cleanup_wave);
}

static uint32_t convert_8(uint8_t i) {
	return ((uint64_t)i * 0xFFFFFFFF) / 0xFF;
}

static uint32_t convert_16(uint16_t i) {
	return ((uint64_t)i * 0xFFFFFFFF) / 0xFFFF;
}

void wave_shift(int channel, int amount) {
	if (amount >= wave_width) {
		if (channel) channel1write = 0;
		else channel0write = 0;
	}
	else {
		uint32_t *ptr = channel ? channel1 : channel0;
		for (int i = 0; i < wave_width - amount; ++i) {
			ptr[i] = ptr[i + amount];
		}
		if (channel) channel1write -= amount;
		else channel0write -= amount;
	}
}

void wave_push_8(uint8_t sample, int channel) {
	if (channel) channel1[channel1write++] = convert_8(sample);
	else channel0[channel0write++] = convert_8(sample);
}

void wave_push_16(uint16_t sample, int channel) {
	if (channel) channel1[channel1write++] = convert_16(sample);
	else channel0[channel0write++] = convert_16(sample);
}

void wave_push_32(uint32_t sample, int channel) {
	if (channel) channel1[channel1write++] = sample;
	else channel0[channel0write++] = sample;
}

float wave_value(int x, int which) {
	uint32_t *waveform = (which ? channel1 : channel0);
	float normalized = waveform[x] / (float)0xFFFFFFFF;

	normalized = (2.f * normalized) - 1.f;
	if (normalized > 0) normalized = 1 - normalized;
	else normalized = -normalized - 1;
	
	return normalized;
}

float wave_y(int x, int which) {
	uint32_t *waveform = (which ? channel1 : channel0);
	float normalized = waveform[x] / (float)0xFFFFFFFF;

	normalized = (2.f * normalized) - 1.f;
	if (normalized > 0) normalized = 1 - normalized;
	else normalized = -normalized - 1;

	return (WAVEFORM_HEIGHT / 2.f) + (normalized * (WAVEFORM_HEIGHT * 0.5f * 0.95f));
}

float wave_speed(int x, int which) {
	if (x < wave_width) {
		return fabs((wave_value(x + 1, which) - wave_value(x, which)) / 2.f);
	}

	return 0.f;
}

float wave_impact(int x, int which, float value) {
	float t = wave_speed(x, which);
	return (1 - value) * t + value;
}